#    Copyright (C) 2007, Maxime Biais <maxime@biais.org>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

from collections import defaultdict
from datetime import date, datetime
import logging
import re
import time

ATOMS = {
    # 1, 2, -345
    'int': '([-+]?\d+)',
    # 1.2, 1, .3, 1., -1.2
    'float': '([-+]?(\d+(\.\d*)?|\.\d+))',
    # 2004-01-31, 2004/01/31, 1982.11.28
    'date1': '((19|20)\d\d([- /.])(0?[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01]))',
    # 2004, 01, 31
    'date2': '((19|20)\d\d([-, /.]+)(0?[1-9]|1[012])([-, /.]+)(0?[1-9]|[12][0-9]|3[01]))',
    # 2004-01-31 10:32:28
    'datetime1': '((19|20)\d\d([- /.])(0?[1-9]|1[012])([- /.])(0[1-9]|[12][0-9]|3[01])\s+(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9]):(0?[0-9]|[1-5][0-9]))',
    }

def date1str_to_date(s):
    s = s.replace(', ', '-').replace('/', '-').replace(' ', '-').replace('.', '-')\

    return date(*(time.strptime(s.replace('/', '-'), '%Y-%m-%d')[0:3])).toordinal()

def datetime1str_to_datetime(s):
    s = s.replace('/', '-').replace(' ', '-').replace('.','-').replace(':','-')
    return datetime(*(time.strptime(s.replace('/', '-'),
                                    '%Y-%m-%d-%H-%M-%S')[0:6]))

FORMATS = [
    # 0: RE, 1: fields, 2: priority
    ['[^\d]*%(int)s\s*$' % ATOMS, ((0, int), ), 0],
    ['\s*%(int)s[\s|]+%(int)s\s*$' % ATOMS, ((0, int), (1, int)), 1],
    ['\s*%(int)s\s+%(float)s\s*$' % ATOMS, ((0, int), (1, float)), 2],
    ['\s%(float)s[^\d]+%(int)s[^\d]*$' % ATOMS, ((0, float), (3, int)),3],
    ['\s*%(float)s\s+%(float)s\s*$' % ATOMS, ((0, float), (3, float)), 4],
    ['\s*%(date1)s[^-\d]+%(int)s\s*$' % ATOMS, ((0, date1str_to_date),
                                              (6, int)), 5],
    ['\s*%(date1)s[^-\d]+%(float)s\s*$' % ATOMS, ((0, date1str_to_date),
                                          (6, float)), 6],
    ['[^-\d]*%(date2)s[^-\d]+%(int)s[^-\d]*$' % ATOMS,
     ((0, date1str_to_date), (6, int)), 7],
    ['\s*%(datetime1)s[^-\d]+%(int)s\s*$' % ATOMS,
     ((0, datetime1str_to_datetime), (9, int)), 8],
    ['\s*%(datetime1)s[^-\d]+%(float)s\s*$' % ATOMS,
     ((0, datetime1str_to_datetime), (9, float)), 9],
    ['[^\d]*%(int)s[^\d]+%(float)s[^\d]+%(int)s[^\d]*' % ATOMS,
     ((0, int), (2, float), (4, int)), 10],
    ]

def sort_dict(d):
    r = d.items()
    def _cmp(a, b):
        r = cmp(b[1], a[1])
        if r == 0:
            return cmp(a[0][2], b[0][2])
        return r
    r.sort(_cmp)
    return r

class AutoFormatter:
    def __init__(self, infile):
        self.infile = infile
        # compile REs
        for n, format in enumerate(FORMATS):
            FORMATS[n] = tuple([re.compile(format[0])]+format[1:])
        self.stream = open(self.infile, 'r')

    def try_formats(self):
        res = defaultdict(int)
        for line in self.stream:
            for format in FORMATS:
                m = format[0].match(line)
                if m:
                    res[format] += 1
        if not res:
            raise Exception("No format found for your data")
        a = sort_dict(res)
        logging.info("Chosen formatter: %s" % str(a[0][0][2]))
        return a[0][0]

    def format(self):
        format = self.try_formats()
        self.data = []
        self.stream.close()
        self.stream = open(self.infile, 'r')
        for line in self.stream:
            m = format[0].match(line)
            if m:
                self.data.append([i[1](m.groups()[i[0]]) for i in format[1]])

    def __str__(self):
        return "\n".join([str(i) for i in self.data])

    def inject_index_column(self):
        for i, elt in enumerate(self.data):
            elt.insert(0, i)

def auto(infile):
    af = AutoFormatter(infile)
    af.format()
    #af.inject_index_column()
    return af.data

if __name__ == '__main__':
    import sys
    a = auto(sys.argv[1])
    print a
